package com.jml.漏桶算法.自定义注解令牌桶;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

@Aspect
@Component
@Slf4j
public class AopLeakyBucketr {
    /**
     * 需要定义一个容器
     */
    private static volatile ConcurrentHashMap<String, Semaphore> rateLimiters = new ConcurrentHashMap<>();

    /**
     * 环绕通知，异常通知，也需要写释放锁
     *
     * @param pjp
     * @return
     */
    @Around(value = "@annotation(com.jml.漏桶算法.自定义注解令牌桶.JmlLeakyBucket)")
    public Object currentLimit(ProceedingJoinPoint pjp) throws Throwable {
        // 1.获取拦截到目标方法
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 2.获取方法
        Method method = methodSignature.getMethod();
        JmlLeakyBucket leakyBucket = method.getDeclaredAnnotation(JmlLeakyBucket.class);
        // 3.获取注解配置限流的名称
        String name = leakyBucket.name();
        String semaphoreName = StringUtils.isEmpty(name) ? method.getName() : name;
        //4.判断该限流的   信号量是否创建
        Semaphore semaphore = rateLimiters.get(semaphoreName);
        // 5.使用双重检验锁 判断 初始化信号量线程安全性问题
        if (semaphore == null) {
            synchronized (this) {
                if (semaphore == null) {
                    semaphore = new Semaphore(leakyBucket.threads());
                }
                rateLimiters.put(semaphoreName, semaphore);
            }
        }
        try {
            // 6.调用tryAcquire 如果返回是为false  当前AQS状态=0
            boolean result = semaphore.tryAcquire();
            if (!result) {
                // 返回自定义注解上配置的 返回限流提示
                return leakyBucket.msg();
            }
            Object proceed = pjp.proceed();//执行目标方法 getJmlLeakyBucket
            // 被限流的请求 对AQS的状态+1 被限流请求 对AQS状态+1
            semaphore.release();
            return proceed;
        } catch (Exception e) {
            // 封装目标方法如果抛出异常
            semaphore.release();
            return "系统错误!";
        }

    }
}